/***************************2.1: ACK/NACK
**************************** Feng Hong; 2015-12-09*/

package com.ouc.tcp.test;

import java.util.Iterator;

import com.ouc.tcp.client.TCP_Sender_ADT;
import com.ouc.tcp.client.UDT_RetransTask;
import com.ouc.tcp.client.UDT_Timer;
import com.ouc.tcp.message.*;
import com.ouc.tcp.tool.TCP_TOOL;


public class TCP_Sender extends TCP_Sender_ADT {
	
	private TCP_PACKET tcpPack;	//待发送的TCP数据报
	
	private UDT_Timer timer;//RDT3.0计时器，用于超时重传
	
//	private SenderWindow_SR SR_window = new SenderWindow_SR(client);//SR发送方窗口实例化
	
	private SenderWindow_Taho Taho_window = new SenderWindow_Taho(client);//Taho发送方窗口实例化
	
	/*构造函数*/
	public TCP_Sender() {
		super();	//调用超类构造函数
		super.initTCP_Sender(this);		//初始化TCP发送端
	}
	
	@Override
	//可靠发送（应用层调用）：封装应用层数据，产生TCP数据报；需要修改
	public void rdt_send(int dataIndex, int[] appData) {
		
//		//生成TCP数据报（设置序号和数据字段/校验和),注意打包的顺序
//		tcpH.setTh_seq(dataIndex * appData.length / 100 );//Convert the format of "Seq" to: 0, 1, 2, ... 999
//		tcpS.setData(appData);
//		tcpPack = new TCP_PACKET(tcpH, tcpS, destinAddr);		
//		//更新带有checksum的TCP 报文头		
//		tcpH.setTh_sum(CheckSum.computeChkSum(tcpPack));
//		tcpPack.setTcpH(tcpH);
//		
//		//发送TCP数据报
//		udt_send(tcpPack);
//		/*RDT3.0,超时重传*/
//		//发送完数据报后启动计时器
//		timer = new UDT_Timer();  
//		//创建重传任务对象
//		UDT_RetransTask reTrans = new UDT_RetransTask(client,tcpPack);
//		//第一个参数是UDT_RetransTask对象，第二个参数是延迟执行的毫秒数，第三个参数是重复执行的间隔毫秒数
//		timer.schedule(reTrans, 1000, 1000);
//		
//		//等待ACK报文
//		waitACK();
				
		/*
		 * SR选择响应协议版本
		 */
		//生成TCP数据报（设置序号和数据字段/校验和),注意打包的顺序
		tcpH.setTh_seq(dataIndex * appData.length / 100 );//Convert the format of "Seq" to: 0, 1, 2, ... 999
		tcpS.setData(appData);		
		tcpPack = new TCP_PACKET(tcpH, tcpS, destinAddr);
		tcpH.setTh_sum(CheckSum.computeChkSum(tcpPack));
		tcpPack.setTcpH(tcpH);
		
//		while(SR_window.isFull());//当窗口满了，系统层需要等待前面超时或出错的分组重传
		
		while(Taho_window.isFull());//当窗口满了，系统层需要等待前面超时或出错的分组重传
		TCP_PACKET packet = new TCP_PACKET(tcpH, tcpS, destinAddr);
		try {
				//发送分组
//				SR_window.sendPacket(packet.clone());
				
			Taho_window.sendPacket(packet.clone());
		} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		
	}
	
	@Override
	//不可靠发送：将打包好的TCP数据报通过不可靠传输信道发送；仅需修改错误标志
	public void udt_send(TCP_PACKET stcpPack) {
		//设置错误控制标志
		tcpH.setTh_eflag((byte)4);		
		//System.out.println("to send: "+stcpPack.getTcpH().getTh_seq());				
		//发送数据报
		client.send(stcpPack);
	}
	
	@Override
	//需要修改
	public void waitACK() {		
		//循环检查ackQueue
		//循环检查确认号对列中是否有新收到的ACK
		while(true) {
			if(!ackQueue.isEmpty()){
				int currentAck=ackQueue.poll();
				//System.out.println("CurrentAck: "+currentAck);
				if (currentAck == tcpPack.getTcpH().getTh_seq()){
					//收到正确的包，打印该包的序列号，退出忙等
					System.out.println("Clear: "+tcpPack.getTcpH().getTh_seq()+"\n");
					timer.cancel(); //RDT3.0 收到正确包，关闭计时器
					break;
				}else{
					//其他情况则重传，并且继续等待该包的ACK
					System.out.println("Retransmit: "+tcpPack.getTcpH().getTh_seq()+"\n");
					udt_send(tcpPack);
				}
			}
		}
	}


	
	@Override
	//接收到ACK报文：检查校验和，将确认号插入ack队列;NACK的确认号为－1；不需要修改
	public void recv(TCP_PACKET recvPack) {
		
		/*
		 * SR选择响应协议版本
		 * 错误的ACK不处理，认为接收方未收到，超时发方会自动重传；只处理ack正确的分组
		 */
		if(CheckSum.computeChkSum(recvPack) != recvPack.getTcpH().getTh_sum()) {
			System.out.println("corrupt");
			return;
		}
//		SR_window.recvPacket(recvPack);
		
		Taho_window.recvPacket(recvPack);
		//初始代码
		System.out.println("Receive ACK Number： "+ recvPack.getTcpH().getTh_ack());
		ackQueue.add(recvPack.getTcpH().getTh_ack());
	    System.out.println();	
	   
		//处理ACK
		//waitACK();
	}
	
}
